Spring Cloud Feign 去除全局包装、统一异常处理

您所在的位置:网站首页 aop 异常处理 Spring Cloud Feign 去除全局包装、统一异常处理

Spring Cloud Feign 去除全局包装、统一异常处理

2023-08-22 12:43| 来源: 网络整理| 查看: 265

2022 年关于 Spring Cloud 服务间调用组件 Feign 配置的总结。

Maven 依赖:

org.springframework.cloud spring-cloud-starter-openfeign 1. 全局响应包装的拆解

一般来说,我们服务接口响应内容都有一层全局的包装,比如:

{ "code": 0, "message": "成功", "data": { ... } }

在服务间调用时如果在每个 Feign 的接口处定义同样的类比如 Result 再 getData() 不是不行,但是比较繁琐,而且在获取想要的数据前可能需要根据 code、message 判断得到的结果是否符合正确。通过自定义 decoder 可以比较好地解决前一个问题:

// 注入 Jackson 的 ObjectMapper,如果不喜欢 Lombok 可以自行修改 @RequiredArgsConstructor public class UnwrapDecoder implements Decoder { private final ObjectMapper objectMapper; @SneakyThrows @Override public Object decode(Response response, Type type) { Reader reader = response.body().asReader(Charset.defaultCharset()); Result result = objectMapper.readValue(reader, Result.class); // 根据 code 判断操作是否成功 if (ResultCode.isSuccess(result.getCode())) { Object data = result.getData(); JavaType javaType = TypeFactory.defaultInstance().constructType(type); return objectMapper.convertValue(data, javaType); } // 若不成功,抛出业务异常,注意此处的异常会在 DecodeException 中被捕获,后文会处理 throw new BusinessException(result.getCode(), result.getMessage()); } }

使用方式:

@FeignClient(name = "serviceA", configuration = {UnwrapDecoder.class}) ... @GetMapping("/xx") // 无需使用 Result 泛型包装,业务中直接调用 XxVO getXxVO(); ... 2. 上游异常请求统一处理

Feign 在收到响应 HTTP 状态码为 40X、50X 等时会进入ErrorDecoder,我们可以自定义处理器:

@Configuration @RequiredArgsConstructor @Slf4j public class FeignClientErrorDecoder implements ErrorDecoder { private final ObjectMapper objectMapper; @Override public Exception decode(String methodKey, Response response) { try { Reader reader = response.body().asReader(Charset.defaultCharset()); Result result = objectMapper.readValue(reader, Result.class); // 如果上游服务响应符合全局包装约定,再次抛出即可 return new BusinessException(result.getCode(), result.getMessage()); } catch (Exception e) { log.error("Response 转换异常: ", e); return new BusinessException(ResultCode.INTERFACE_INNER_INVOKE_ERROR); } } } 3. 关于 Feign 的全局异常处理

前文提到,UnwrapDecoder 中抛出的业务异常会被捕获并以 DecodeException 抛出,因此在全局异常中单独处理(此类可以与常规的全局异常处理类分开共存):

@RestControllerAdvice @Slf4j @Order(Ordered.HIGHEST_PRECEDENCE) // 优先级 @ResponseStatus(code = HttpStatus.BAD_REQUEST) // 统一 HTTP 状态码 @RequiredArgsConstructor public class FeignExceptionHandler { /** * 拦截 FeignException 异常,Jackson 处理失败等情况会进入 */ @ExceptionHandler(FeignException.class) public Result handleFeignException(FeignException e) { log.error("FeignException: ", e); return Result.errorResult(ResultCode.INTERFACE_INNER_INVOKE_ERROR, e.getMessage()); } /** * 拦截 DecodeException 异常,decoder 中抛出的自定义全局异常会进入此处 */ @ExceptionHandler(DecodeException.class) public Result handleDecodeException(DecodeException e) { Throwable cause = e.getCause(); if (cause instanceof BusinessException) { BusinessException businessException = (BusinessException) cause; // 上游符合全局响应包装约定的再次抛出即可 return Result.errorResult(businessException.getCode(), businessException.getMessage()); } log.error("DecodeException: ", e); return Result.errorResult(ResultCode.INTERFACE_INNER_INVOKE_ERROR, e.getMessage()); } }

至此,框架自动转交上游服务的业务异常,业务中不必关注全局包装的拆解问题,完美!



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3